package com.aries.rpc.spring.starter.domain;

import com.aries.dubbo.like.rpc.client.proxy.ServiceProxy;
import com.aries.dubbo.like.rpc.common.annotations.RpcConsumer;
import com.aries.dubbo.like.rpc.common.annotations.RpcProvider;
import com.aries.dubbo.like.rpc.common.context.RpcServerContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.ComponentScan;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;

@Slf4j
@SuppressWarnings("all")
public class AnnotationBean implements DisposableBean, BeanFactoryPostProcessor, BeanPostProcessor, ApplicationContextAware {

    private ApplicationContext applicationContext;
    private String basePath;

    @Override
    public void destroy() throws Exception {

    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        if (beanFactory instanceof BeanDefinitionRegistry) {
            try {
                Class<?> scannerClass = Class.forName("org.springframework.context.annotation.ClassPathBeanDefinitionScanner");
                Object scanner = scannerClass.getConstructor(new Class<?>[]{BeanDefinitionRegistry.class, boolean.class}).newInstance(new Object[]{beanFactory, true});
                Class<?> filterClass = Class.forName("org.springframework.core.type.filter.AnnotationTypeFilter");
                Object filter = filterClass.getConstructor(Class.class).newInstance(RpcProvider.class);
                Method addIncludeFilter = scannerClass.getMethod("addIncludeFilter", Class.forName("org.springframework.core.type.filter.TypeFilter"));
                addIncludeFilter.invoke(scanner, filter);
                // scan packages
                Method scan = scannerClass.getDeclaredMethod("scan", new Class<?>[]{String[].class});
                String[] basePackages = new String[1];
                basePackages[0] = this.basePath;

                scan.invoke(scanner, new Object[]{basePackages});
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
//        Object object = applicationContext.getBeansWithAnnotation(SpringBootApplication.class).values().toArray()[0];
//        ComponentScan componentScan = object.getClass().getDeclaredAnnotation(ComponentScan.class);
//        this.basePath = componentScan.value()[0];
    }

    /**
     * 为bean中被rpcConsumer注解的field赋值。（为接口生成动态代理实现类）
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Field[] fields = bean.getClass().getDeclaredFields();
        Arrays.stream(fields).forEach(field -> {
            if (field.isAnnotationPresent(RpcConsumer.class)) {
                field.setAccessible(true);
                RpcConsumer rpcConsumer = field.getDeclaredAnnotation(RpcConsumer.class);
                if (rpcConsumer.interfaceClass() != Void.class) {
                    try {
                        field.set(bean, ServiceProxy.newInstance(rpcConsumer.interfaceClass()));
                        log.info("set field:" + field.getName() + " which annotated with RpcConsumer");
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                } else {
                    try {
                        field.set(bean, ServiceProxy.newInstance(field.getType().getClass().getInterfaces()[0]));
                        log.info("set rpc consumer field: " + field.getName());
                    } catch (IllegalAccessException e) {
                        log.error("set field failed,because of:", e);
                    }
                }
            }
        });

        if (bean.getClass().isAnnotationPresent(RpcProvider.class)) {
            log.info("add bean and bean class name into rpcServerContext. bean: " + bean.getClass().getInterfaces()[0].getName());
            RpcServerContext.add(bean.getClass().getInterfaces()[0].getName(), bean);
        }

        return bean;
    }

    /**
     * 为rpc provider 中被 @Autowired 注解的field赋值。
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        Class<?> clz = bean.getClass();
        for (Field field : clz.getDeclaredFields()) {
            if (field.isAnnotationPresent(Autowired.class)) {
                field.setAccessible(true);
                try {
                    if (clz.isAnnotationPresent(RpcProvider.class)) {

                        field.set(bean, applicationContext.getBean(field.getType()));
                        log.info("autowird field:" + field.getName());
//
//                        if (field.getType().getClass().getInterfaces().length == 0) {
//                            System.out.println("111111111");
////                            log.info("try to autowire field:" + field.getName() + " as " + applicationContext.getBean(field.getType().getClass()).getName());
//                            field.set(bean, applicationContext.getBean(field.getType().getClass()));
//                        } else {
//                            System.out.println("2222222222");
////                            log.info("try to autowire field:" + field.getName() + " as " + applicationContext.getBean(field.getType().getClass().getInterfaces()[0].getClass()).getName());
//                            field.set(bean, applicationContext.getBean(field.getType().getClass().getInterfaces()[0]));
//                        }
                    }
                } catch (Exception e) {
                    log.error("autowire faild,because of :" + e);
                }
            }
        }
        return bean;
    }

    public void setBasePath(String basePath1) {
        this.basePath = basePath1;
    }
}